/**
 * Package:com.goldcitynet.server.data;
 * $Id: ActivedCharacter.java,v 1.39 2009/05/11 02:20:28 yangc Exp $
 * Copyright(c) 2001-2005 www.afteryuan.com
 */
package com.afteryuan.server.data;

import com.afteryuan.server.command.model.Pane;
import com.afteryuan.server.ConnectSession;

import java.util.Arrays;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
import java.io.Serializable;

/**
 * Sprite对象存储和维护每个注册到服务器的可移动对象
 * 只负责维护引擎必要数据,不与具体的业务逻辑挂钩.
 * 具体的业务对象可以通过属性列表与Sprite对象绑定
 * <p/>
 * <p><a href="Sprite.java.html"><i>View Source</i></a></p>
 *
 * @author <a href="mailto:afteryuan@gmail.com">$Author: Spires Zhang $</a>
 * @version $Revision:$
 */
public class Sprite implements Serializable{
    public final static String KEY_SPRITE_ID="KEY_SPRITE_ID";
    /**
     * 一般以用户的主键为ID
     */
    private String id;
    /**
     * 血量 
     */
    private Integer hp = 500;
    /**
     * 当前x坐标
     */ 
    private Integer cx = 0;
    /**
     * 当前y坐标
     */
    private Integer cy = 22;
    /**
     * 目标x坐标
     */
    private Integer dx = 0;
    /**
     * 目标y坐标
     */
    private Integer dy = 0;

    /**
     * 延迟
     */
    private Integer delay = 0;
    
    /**
     * 速度
     */
    private Integer speed = 1;
    /**
     * 移动路径
     */
    private Pane[] panes;

    /**
     * 计算delay值所需要的ping值数量（常量）
     */
    private static final Integer PINGSIZE = 30;
    /**
     * ping值数组
     */
    private Integer[] pings = new Integer[PINGSIZE];
    /**
     * 接收的ping包的数量
     */
    private Long pingCount = 0L;

    /**
     * 所处空间
     */
    private Room room;

    /**
     * 动作状态
     */
    private Integer actionType = 0;

    /**
     * 移动状态
     */
    private Integer moveType = 0;

    /**
     * 朝向状态
     */
    private Integer statesType = Pane.DIRECT_DOWN_RIGHT;

    /**
     * 是否暂离状态。默认为false。
     */
    private boolean off = false;

    /**
     * 暂离时间点
     */
    private long offTime = 0L;

    private boolean cache;

    /**
     * 属性列表
     */
    private Map attritubes = new Hashtable();

    /**
     * 连接Session
     */
    private ConnectSession connectSession = null;



    /**********************  getter and setter methods ************************/
    public String getId() {
        return id;
    }

    public void setId(String id) {
        if(isCache())throw new IllegalStateException("This sprite is in cache,Do invoke this method.");
        this.id = id;
    }

    public Integer getCx() {
        return cx;
    }

    public void setCx(Integer cx) {
        this.cx = cx;
    }

    public Integer getCy() {
        return cy;
    }

    public void setCy(Integer cy) {
        this.cy = cy;
    }

    public Integer getDx() {
        return dx;
    }

    public void setDx(Integer dx) {
        this.dx = dx;
    }

    public Integer getDy() {
        return dy;
    }

    public void setDy(Integer dy) {
        this.dy = dy;
    }

    public Integer getDelay() {
        return delay;
    }

    public void setDelay(Integer delay) {
        this.delay = delay;
    }

    public Integer getSpeed() {
        return speed;
    }

    public void setSpeed(Integer speed) {
        this.speed = speed;
    }

    public Pane[] getPanes() {
        return panes;
    }

    public void setPanes(Pane[] panes) {
        this.panes = panes;
    }

    public Integer[] getPings() {
        return pings;
    }

    public void setPings(Integer[] pings) {
        this.pings = pings;
    }

    public Long getPingCount() {
        return pingCount;
    }

    public void setPingCount(Long pingCount) {
        this.pingCount = pingCount;
    }

    public Room getRoom() {
        return room;
    }

    public void setRoom(Room room) {
        if(isCache())throw new IllegalStateException("This sprite is in cache,Do invoke this method.");
        this.room = room;
    }

    public Integer getActionType() {
        return actionType;
    }

    public void setActionType(Integer actionType) {
        this.actionType = actionType;
    }

    public Integer getMoveType() {
        return moveType;
    }

    public void setMoveType(Integer moveType) {
        this.moveType = moveType;
    }

    public Integer getStatesType() {
        return statesType;
    }

    public void setStatesType(Integer statesType) {
        this.statesType = statesType;
    }

    public boolean isOff() {
        return off;
    }

    public void setOff(boolean off) {
        this.off = off;
    }

    public long getOffTime() {
        return offTime;
    }

    public void setOffTime(long offTime) {
        this.offTime = offTime;
    }

    public boolean isCache() {
        return cache;
    }

    public void setCache(boolean cache) {
        this.cache = cache;
    }

    public Map getAttritubes() {
        return attritubes;
    }

    public void setAttritubes(Map attritubes) {
        this.attritubes = attritubes;
    }

    public ConnectSession getConnectSession() {
        return connectSession;
    }

    public void setConnectSession(ConnectSession connectSession) {
        this.connectSession = connectSession;
    }

    public long getMoveUpdateTime() {
        return moveUpdateTime;
    }

    public void setMoveUpdateTime(long moveUpdateTime) {
        this.moveUpdateTime = moveUpdateTime;
    }

    public int getAmend() {
        return amend;
    }

    public void setAmend(int amend) {
        this.amend = amend;
    } /***************************** methods ************************************/
    /**
     * 添加ping值
     *
     * @param ping ping值
     */
    public void addPings(Integer ping) {
        pings[((int) (pingCount % PINGSIZE))] = ping;
        pingCount++;
    }

    /**
     * 计算延迟
     */
    public void calculateDelay() {
        Integer tmp = 0;
        Long count = pingCount < PINGSIZE ? pingCount : PINGSIZE;
        for (int i = 0; i < count; i++) {
            tmp += pings[i];
        }
        delay = Long.valueOf(tmp / count).intValue();
    }

    public void updatePanes(Pane[] panes) {
        this.panes = panes;
        this.moveUpdateTime = new Date().getTime();
    }

    /**
     * 及时更新
     */
    public void update() {
        if (panes == null || panes.length == 0) return;
        update((int) (new Date().getTime() - moveUpdateTime));
    }

    
    private long moveUpdateTime;

    /**
     * 更新用户当前坐标及剩余寻路路径
     *
     * @param stepTime 间隔时间（由SynManager定义，现为500ms）
     */
    public void update(Integer stepTime) {
        this.moveUpdateTime = new Date().getTime();
        stepTime += delay;
        int step = 0;    //本次同步人物移动的步数
        int times = stepTime / 200;  //本次同步基本移动次数
        times = times + amend;
        amend = 0;//修正值已经使用，归零
        int i = 0;
        for (i = 0; i < times && step < panes.length; i++) {
            next(panes[step]);  //移动一步
            if (panes[step].direction == Pane.DIRECT_UP_RIGHT || panes[step].direction == Pane.DIRECT_DOWN_LEFT) {
                i++;    //当方向是右上或者左下是消耗两个基本移动次数单元。
            }
            step++;                            //步数增加。
            if (!isMoving()) break;
        }
        if (i > times) amend = -1;//本次同步多走了一个times，故修正值改为-1.
        int length = panes.length > step ? panes.length - step : 0;
        if (length == 0) {
            cx = dx;
            cy = dy;
        }
//        swap = Factory.sceneLoader.getSceneData(sceneId)[cy][cx];
        Pane[] newPanes = new Pane[length];
        System.arraycopy(panes, panes.length > step ? step : 0, newPanes, 0, length);
        panes = newPanes;
    }

    private int amend = 0;

    private void next(Pane p) {
        this.statesType = p.direction;//每走一步，更新其朝向状态。
        switch (p.direction) {
            case Pane.DIRECT_UP:
                cy--;
                break;
            case Pane.DIRECT_UP_RIGHT:
                cy--;
                cx++;
                break;
            case Pane.DIRECT_RIGHT:
                cx++;
                break;
            case Pane.DIRECT_DOWN_RIGHT:
                cy++;
                cx++;
                break;
            case Pane.DIRECT_DOWN:
                cy++;
                break;
            case Pane.DIRECT_DOWN_LEFT:
                cx--;
                cy++;
                break;
            case Pane.DIRECT_LEFT:
                cx--;
                break;
            case Pane.DIRECT_UP_LEFT:
                cy--;
                cx--;
                break;
        }
    }


    /**
     * 是否正在移动
     *
     * @return boolean
     */
    public boolean isMoving() {
        return !(cx.equals(dx) && cy.equals(dy));
    }

    /**
     * 是否相等，比较标准为id相等
     *
     * @param o 目标对象
     * @return boolean
     */
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Sprite that = (Sprite) o;

        return !(id != null ? !id.equals(that.id) : that.id != null);

    }

    /**
     * return hashCode
     *
     * @return int
     */
    public int hashCode() {
        return (id != null ? id.hashCode() : 0);
    }

    @Override
    public String toString() {
        return "Sprite{" +
                "id='" + id + '\'' +
                ", cx=" + cx +
                ", cy=" + cy +
                ", dx=" + dx +
                ", dy=" + dy +
                ", delay=" + delay +
                ", speed=" + speed +
                ", panes=" + (panes == null ? null : Arrays.asList(panes)) +
                ", pings=" + (pings == null ? null : Arrays.asList(pings)) +
                ", pingCount=" + pingCount +
                ", room=" + room +
                ", actionType=" + actionType +
                ", moveType=" + moveType +
                ", statesType=" + statesType +
                ", off=" + off +
                ", offTime=" + offTime +
                ", attritubes=" + attritubes +
                ", connectSession=" + connectSession +
                ", moveUpdateTime=" + moveUpdateTime +
                ", amend=" + amend +
                '}';
    }

	public Integer getHp() {
		return hp;
	}

	public void setHp(Integer hp) {
		this.hp = hp;
	}
}